Add LangChain TinyFish integration#14
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✅ Files skipped from review due to trivial changes (2)
📝 WalkthroughWalkthroughThis PR adds a packaged langchain-tinyfish integration: a Pydantic TinyFishAPIWrapper (sync/async, SSE, management helpers), four LangChain tools (web automation with optional LangGraph SSE streaming, search, fetch, browser session), pyproject packaging and dev requirements, Makefile and import-check script, CI and conditional PyPI publish workflows, README and MIT LICENSE, and comprehensive unit and integration tests. Sequence Diagram(s)sequenceDiagram
participant Agent as LangChain Agent
participant Tool as TinyFishWebAutomation
participant Wrapper as TinyFishAPIWrapper
participant SDK as TinyFish SDK
participant Writer as LangGraph StreamWriter
Agent->>Tool: invoke(url, goal)
Tool->>Wrapper: run_sse(url, goal) or run(url, goal)
Wrapper->>SDK: agent.stream / agent.run (start run)
SDK->>Wrapper: SSE events (STARTED, STREAMING_URL, PROGRESS, COMPLETE)
Wrapper->>Tool: normalized events
Tool->>Writer: write(streaming_url / progress) (when writer available)
Wrapper->>Tool: result JSON from COMPLETE handling
Tool-->>Agent: return result
Suggested reviewers
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsGit: Failed to clone repository. Please run the Comment |
Bumps the TinyFish Python SDK dependency to 0.2.5 and exposes SDK-backed LangChain tools for web search, content fetch, and browser session creation. Updates package metadata, README examples, unit coverage, and adds LangChain CI plus PyPI trusted-publishing workflows.
b9cc376 to
46fedc9
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
langchain/tests/integration_tests/test_standard.py (1)
12-27:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd API-key skip guard for this integration harness.
This integration suite currently lacks the
TINYFISH_API_KEYskip condition used in the other integration tests, so it can fail in environments where secrets are intentionally absent.Proposed patch
from __future__ import annotations +import os from typing import Any +import pytest from langchain_tests.integration_tests import ToolsIntegrationTests from langchain_tinyfish import TinyFishWebAutomation +skip_no_key = pytest.mark.skipif( + not os.environ.get("TINYFISH_API_KEY"), + reason="TINYFISH_API_KEY not set", +) + +@skip_no_key class TestTinyFishToolIntegration(ToolsIntegrationTests):🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@langchain/tests/integration_tests/test_standard.py` around lines 12 - 27, The TestTinyFishToolIntegration class lacks the environment-key skip guard used in other integration tests; add a skip condition that checks for the TINYFISH_API_KEY env var and skips the whole class when it's missing (e.g., using pytestmark = pytest.mark.skipif(os.environ.get("TINYFISH_API_KEY") is None, reason="TINYFISH_API_KEY not set") or an equivalent pytest.skip mechanism). Update the top of the test class TestTinyFishToolIntegration so the guard runs before any tests or property access, and import or reference pytest and os as needed to implement the check.
🧹 Nitpick comments (2)
langchain/tests/unit_tests/test_packaging.py (1)
14-16: ⚡ Quick winMake the tinyfish floor check resilient to additional constraints.
Line 16 requires an exact string match, so a valid change like
tinyfish>=0.2.5,<1would fail this test unnecessarily.💡 Suggested fix
dependencies = pyproject["project"]["dependencies"] - - assert "tinyfish>=0.2.5" in dependencies + tinyfish_dep = next((dep for dep in dependencies if dep.startswith("tinyfish")), None) + assert tinyfish_dep is not None + assert ">=0.2.5" in tinyfish_dep🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@langchain/tests/unit_tests/test_packaging.py` around lines 14 - 16, The test currently asserts an exact string match against dependencies, which fails when extra version specifiers are present; update the assertion that references the dependencies variable in test_packaging.py to check that at least one dependency entry corresponds to tinyfish and includes the minimum floor ">=0.2.5" (for example by using any(...) over dependencies to find an entry that starts with "tinyfish" or matches the package name and contains ">=0.2.5"), so the test accepts entries like "tinyfish>=0.2.5,<1"..github/workflows/langchain-ci.yml (1)
35-39: ⚡ Quick winAdd a packaging build smoke-check in CI.
The workflow validates lint/tests but skips
python -m build, so packaging breakages can slip through until release workflows.💡 Suggested fix
- run: python -m pip install -e . -r requirements-dev.txt @@ - run: make test + - run: python -m pip install build + - run: python -m build🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/langchain-ci.yml around lines 35 - 39, Add a packaging smoke-check step to the CI by inserting a new job step that runs package build (e.g., install the build tool and invoke python -m build) after installing deps and before/after lint/tests; specifically ensure the workflow runs something like `python -m pip install build` (or include build in requirements-dev) and then `python -m build` so packaging errors are surfaced in CI—update the workflow steps adjacent to the existing `python -m pip install -e . -r requirements-dev.txt`, `make lint`, and `make test` commands.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@langchain/langchain_tinyfish/tool.py`:
- Around line 326-340: The _run and _arun methods currently forward any number
of URLs to api_wrapper.fetch despite TinyFishFetchInput documenting 1–10 URLs;
add a fast pre-call validation in both TinyFishTool._run and TinyFishTool._arun
that checks len(urls) is between 1 and 10 and raise a clear ValueError (e.g.
"urls must contain between 1 and 10 items") before calling
self.api_wrapper.fetch; reference the TinyFishFetchInput contract and ensure the
same check and error message are applied to both synchronous (_run) and
asynchronous (_arun) paths so invalid payloads fail predictably.
- Around line 224-228: The SSE consumer loops calling self.api_wrapper.run_sse
are not stopping when a terminal event is received; modify the loops that call
self._dispatch_event(writer, event) (the ones iterating over
self.api_wrapper.run_sse) so that when _dispatch_event returns a non-None
terminal value (e.g., the COMPLETE sentinel) you assign result and then
immediately break out of the for loop (or return) to stop consuming the stream;
apply the same change to both occurrences that handle SSE (the loop using writer
and the similar loop around lines 254-258) so the stream is not read after a
terminal event.
---
Outside diff comments:
In `@langchain/tests/integration_tests/test_standard.py`:
- Around line 12-27: The TestTinyFishToolIntegration class lacks the
environment-key skip guard used in other integration tests; add a skip condition
that checks for the TINYFISH_API_KEY env var and skips the whole class when it's
missing (e.g., using pytestmark =
pytest.mark.skipif(os.environ.get("TINYFISH_API_KEY") is None,
reason="TINYFISH_API_KEY not set") or an equivalent pytest.skip mechanism).
Update the top of the test class TestTinyFishToolIntegration so the guard runs
before any tests or property access, and import or reference pytest and os as
needed to implement the check.
---
Nitpick comments:
In @.github/workflows/langchain-ci.yml:
- Around line 35-39: Add a packaging smoke-check step to the CI by inserting a
new job step that runs package build (e.g., install the build tool and invoke
python -m build) after installing deps and before/after lint/tests; specifically
ensure the workflow runs something like `python -m pip install build` (or
include build in requirements-dev) and then `python -m build` so packaging
errors are surfaced in CI—update the workflow steps adjacent to the existing
`python -m pip install -e . -r requirements-dev.txt`, `make lint`, and `make
test` commands.
In `@langchain/tests/unit_tests/test_packaging.py`:
- Around line 14-16: The test currently asserts an exact string match against
dependencies, which fails when extra version specifiers are present; update the
assertion that references the dependencies variable in test_packaging.py to
check that at least one dependency entry corresponds to tinyfish and includes
the minimum floor ">=0.2.5" (for example by using any(...) over dependencies to
find an entry that starts with "tinyfish" or matches the package name and
contains ">=0.2.5"), so the test accepts entries like "tinyfish>=0.2.5,<1".
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ac0cfb0f-4b22-4ad1-98b5-2281cf3cdfce
📒 Files selected for processing (24)
.github/workflows/langchain-ci.yml.github/workflows/langchain-publish.ymllangchain/.gitignorelangchain/LICENSElangchain/Makefilelangchain/README.mdlangchain/langchain_tinyfish/__init__.pylangchain/langchain_tinyfish/_api_wrapper.pylangchain/langchain_tinyfish/py.typedlangchain/langchain_tinyfish/tool.pylangchain/pyproject.tomllangchain/requirements-dev.txtlangchain/scripts/check_imports.pylangchain/tests/__init__.pylangchain/tests/integration_tests/__init__.pylangchain/tests/integration_tests/test_compile.pylangchain/tests/integration_tests/test_integration.pylangchain/tests/integration_tests/test_standard.pylangchain/tests/unit_tests/__init__.pylangchain/tests/unit_tests/test_api_wrapper.pylangchain/tests/unit_tests/test_imports.pylangchain/tests/unit_tests/test_packaging.pylangchain/tests/unit_tests/test_standard.pylangchain/tests/unit_tests/test_tool.py
Stop SSE consumption after terminal events, validate fetch URL counts, skip integration tests without credentials, and add CI build coverage.
Add the build package to LangChain dev requirements so the CI packaging smoke test can run python -m build.
Align the LangChain integration with the ADK package release path: push-to-main builds the package, skips already-published versions, and uploads with the provisioned PyPI token. Also align Python metadata with the tinyfish SDK floor before the first publish.\n\nConstraint: tinyfish 0.2.5 requires Python >=3.11 and PYPI_API_TOKEN is the repo secret provisioned through github-control.\nRejected: tag-only trusted publishing | this repo is using first-release publish-on-main for these integration packages.\nConfidence: high\nScope-risk: narrow\nTested: YAML parsed; PyPI guard returned missing for langchain-tinyfish 0.1.0; uv build --python 3.13; twine check; make lint; make test.\nNot-tested: live GitHub Actions publish, because it only runs after merge to main.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/langchain-publish.yml:
- Around line 90-106: The publish-pypi job is using a long-lived secret via the
password field; switch it to OIDC Trusted Publishing by adding a job-level
permissions block that sets id-token: write and removing the explicit password
input for the pypa/gh-action-pypi-publish step. Update the job named
publish-pypi to include permissions: id-token: write and delete the line
referencing password: ${{ secrets.PYPI_API_TOKEN }} so the
pypa/gh-action-pypi-publish action will use OIDC instead of the stored secret.
- Around line 31-76: The publish step currently uses the
pypa/gh-action-pypi-publish action and is vulnerable to a TOCTOU race; update
the "Publish to PyPI" step that invokes pypa/gh-action-pypi-publish to include
the input skip-existing: true so the action tolerates duplicate uploads if
another run already pushed the same version. Locate the step that uses
pypa/gh-action-pypi-publish and add the skip-existing: true parameter to the
action inputs (you can keep the preflight PyPI check but ensure skip-existing is
set).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: fc09c049-97b5-4799-8800-bfce9e7b8d86
📒 Files selected for processing (3)
.github/workflows/langchain-publish.ymllangchain/pyproject.tomllangchain/tests/unit_tests/test_packaging.py
🚧 Files skipped from review as they are similar to previous changes (2)
- langchain/pyproject.toml
- langchain/tests/unit_tests/test_packaging.py
| - name: Check PyPI version availability | ||
| id: version | ||
| working-directory: ./langchain | ||
| run: | | ||
| PACKAGE=$(python -c "import tomllib; f=open('pyproject.toml','rb'); d=tomllib.load(f); print(d['project']['name'])") | ||
| VERSION=$(python -c "import tomllib; f=open('pyproject.toml','rb'); d=tomllib.load(f); print(d['project']['version'])") | ||
| echo "package=${PACKAGE}" >> "$GITHUB_OUTPUT" | ||
| echo "version=${VERSION}" >> "$GITHUB_OUTPUT" | ||
| RESULT=$(python - "$PACKAGE" "$VERSION" <<'PY' | ||
| import sys | ||
| import urllib.error | ||
| import urllib.parse | ||
| import urllib.request | ||
|
|
||
| package = sys.argv[1] | ||
| version = sys.argv[2] | ||
| url = f"https://pypi.org/pypi/{urllib.parse.quote(package)}/{version}/json" | ||
| try: | ||
| with urllib.request.urlopen(url, timeout=10): | ||
| pass | ||
| except urllib.error.HTTPError as exc: | ||
| if exc.code == 404: | ||
| print("missing") | ||
| sys.exit(0) | ||
| print(f"PyPI returned HTTP {exc.code} while checking {url}", file=sys.stderr) | ||
| sys.exit(2) | ||
| except Exception as exc: | ||
| print(f"Failed to check PyPI for {url}: {exc}", file=sys.stderr) | ||
| sys.exit(2) | ||
| print("exists") | ||
| PY | ||
| ) | ||
| case "$RESULT" in | ||
| exists) | ||
| echo "exists=true" >> "$GITHUB_OUTPUT" | ||
| echo "${PACKAGE} ${VERSION} already exists on PyPI; skipping publish." | ||
| ;; | ||
| missing) | ||
| echo "exists=false" >> "$GITHUB_OUTPUT" | ||
| echo "${PACKAGE} ${VERSION} is available on PyPI; building distribution." | ||
| ;; | ||
| *) | ||
| echo "Unexpected PyPI availability result: $RESULT" | ||
| exit 1 | ||
| ;; | ||
| esac |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "langchain-publish.yml" -type fRepository: tinyfish-io/tinyfish-web-agent-integrations
Length of output: 129
🏁 Script executed:
cat -n .github/workflows/langchain-publish.yml 2>/dev/null || echo "File not found in root"Repository: tinyfish-io/tinyfish-web-agent-integrations
Length of output: 4227
🌐 Web query:
pypa gh-action-pypi-publish skip-existing parameter documentation
💡 Result:
The pypa/gh-action-pypi-publish action documents the input parameter as skip-existing, disabled by default, and it can be enabled in the workflow as: with: skip-existing: true [1][2] The official action repository explains that skip-existing is intended to tolerate release package file duplicates (e.g., race conditions when publishing the same release from multiple places), and includes a note advising to avoid enabling it when possible. [1] In the action.yml, the input is wired through to the underlying implementation via skip-existing: ${{ inputs.skip-existing || inputs.skip_existing }}. [3]
Citations:
- 1: https://github.com/pypa/gh-action-pypi-publish
- 2: https://github.com/marketplace/actions/pypi-publish
- 3: https://github.com/pypa/gh-action-pypi-publish/blob/06c7e00941a1013bdd44f6d1d47551d3d1d461ae/action.yml
Make the publish step idempotent.
The preflight PyPI check creates a TOCTOU window: two concurrent workflow runs can both observe the version as missing before either publishes, causing the second upload to fail. pypa/gh-action-pypi-publish supports skip-existing: true to tolerate duplicate releases, so add it to the publish step.
🛠️ Proposed fix
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist/
+ skip-existing: true
password: ${{ secrets.PYPI_API_TOKEN }}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/langchain-publish.yml around lines 31 - 76, The publish
step currently uses the pypa/gh-action-pypi-publish action and is vulnerable to
a TOCTOU race; update the "Publish to PyPI" step that invokes
pypa/gh-action-pypi-publish to include the input skip-existing: true so the
action tolerates duplicate uploads if another run already pushed the same
version. Locate the step that uses pypa/gh-action-pypi-publish and add the
skip-existing: true parameter to the action inputs (you can keep the preflight
PyPI check but ensure skip-existing is set).
| publish-pypi: | ||
| name: Publish to PyPI | ||
| needs: build | ||
| if: needs.build.outputs.version-exists == 'false' | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Download distributions | ||
| uses: actions/download-artifact@v8 | ||
| with: | ||
| name: python-package-distributions | ||
| path: dist/ | ||
|
|
||
| - name: Publish to PyPI | ||
| uses: pypa/gh-action-pypi-publish@release/v1 | ||
| with: | ||
| packages-dir: dist/ | ||
| password: ${{ secrets.PYPI_API_TOKEN }} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's find and examine the workflow file
find . -name "langchain-publish.yml" -type fRepository: tinyfish-io/tinyfish-web-agent-integrations
Length of output: 129
🏁 Script executed:
# Get the specific lines from the workflow file
if [ -f ".github/workflows/langchain-publish.yml" ]; then
cat -n ".github/workflows/langchain-publish.yml" | sed -n '90,110p'
fiRepository: tinyfish-io/tinyfish-web-agent-integrations
Length of output: 696
🌐 Web query:
gh-action-pypi-publish OIDC trusted publishing configuration
💡 Result:
Trusted Publishing (using OpenID Connect) allows you to publish Python packages to PyPI from GitHub Actions without storing long-lived API tokens or passwords as GitHub Secrets [1][2]. To configure Trusted Publishing with the pypa/gh-action-pypi-publish action, follow these steps: 1. Configure the Trusted Publisher on PyPI Navigate to your project's settings on PyPI (e.g., https://pypi.org/manage/project//settings/publishing/) [3][4]. Add a new trusted publisher by specifying the GitHub repository owner, repository name, and the workflow filename (e.g., release.yml) that will perform the publishing [3][4]. You can optionally restrict this to a specific GitHub Actions environment [3][4]. 2. Update your GitHub Actions Workflow In your GitHub Actions workflow file, you must grant the publishing job the necessary OIDC permissions and omit any username or password inputs for the action [3][2]. Required configuration in your workflow YAML: - Set the id-token permission to write at the job level [3][5]. - Use the pypa/gh-action-pypi-publish action without providing a username or password [2][6]. Example Workflow Snippet: jobs: pypi-publish: runs-on: ubuntu-latest environment: release permissions: id-token: write # Mandatory for Trusted Publishing steps: - uses: actions/download-artifact@v5 with: name: release-dists path: dist/ - uses: pypa/gh-action-pypi-publish@release/v1 Important Considerations: - Security: Trusted Publishing is highly recommended over long-lived API tokens because it uses short-lived, automatically expiring tokens, significantly reducing the risk of credential theft [1][7]. - Reusable Workflows: Trusted Publishing is currently not supported when called from within a reusable workflow [6]. It is recommended to keep the job that calls pypa/gh-action-pypi-publish in a top-level workflow [6]. - Isolation: It is best practice to keep your build and publish steps in separate jobs, with the publish job having the minimum necessary privileges [2].
Citations:
- 1: https://docs.pypi.org/trusted-publishers/
- 2: https://github.com/pypa/gh-action-pypi-publish
- 3: https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-pypi
- 4: https://docs.github.com/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-pypi
- 5: https://docs.pypi.org/trusted-publishers/using-a-publisher/
- 6: https://github.com/PyPA/gh-action-PyPI-publish
- 7: https://dev.to/snyk/malicious-release-of-elementary-data-pypi-package-steals-cloud-credentials-from-data-engineers-1gdk
Switch this job to OIDC Trusted Publishing.
The job currently uses password: ${{ secrets.PYPI_API_TOKEN }} without declaring permissions.id-token: write, keeping a long-lived secret in the release path. OIDC Trusted Publishing provides superior security by using short-lived, automatically expiring tokens instead of long-lived API tokens. To enable it, add permissions: id-token: write at the job level and remove the explicit password field.
Proposed fix
publish-pypi:
name: Publish to PyPI
needs: build
if: needs.build.outputs.version-exists == 'false'
runs-on: ubuntu-latest
+ permissions:
+ id-token: write
steps:
- name: Download distributions
uses: actions/download-artifact@v8
with:
name: python-package-distributions
path: dist/
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist/
- password: ${{ secrets.PYPI_API_TOKEN }}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| publish-pypi: | |
| name: Publish to PyPI | |
| needs: build | |
| if: needs.build.outputs.version-exists == 'false' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Download distributions | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: python-package-distributions | |
| path: dist/ | |
| - name: Publish to PyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| packages-dir: dist/ | |
| password: ${{ secrets.PYPI_API_TOKEN }} | |
| publish-pypi: | |
| name: Publish to PyPI | |
| needs: build | |
| if: needs.build.outputs.version-exists == 'false' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| id-token: write | |
| steps: | |
| - name: Download distributions | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: python-package-distributions | |
| path: dist/ | |
| - name: Publish to PyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| packages-dir: dist/ |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/langchain-publish.yml around lines 90 - 106, The
publish-pypi job is using a long-lived secret via the password field; switch it
to OIDC Trusted Publishing by adding a job-level permissions block that sets
id-token: write and removing the explicit password input for the
pypa/gh-action-pypi-publish step. Update the job named publish-pypi to include
permissions: id-token: write and delete the line referencing password: ${{
secrets.PYPI_API_TOKEN }} so the pypa/gh-action-pypi-publish action will use
OIDC instead of the stored secret.
Keep release-process guidance outside package READMEs so it does not become PyPI project description content.\n\nConstraint: Python publish workflows read versions from pyproject.toml and use the repo PYPI_API_TOKEN secret.\nRejected: package README docs | package READMEs are published as public PyPI descriptions.\nConfidence: high\nScope-risk: narrow\nTested: git diff --check .github/README.md.\nNot-tested: GitHub Actions, documentation-only change.
Make the workflow subtree preserve release-process guidance outside package READMEs, which are reused as PyPI descriptions.\n\nConstraint: .github guidance applies only to repo automation and maintainer docs.\nRejected: package-level guidance | package READMEs are public package metadata.\nConfidence: high\nScope-risk: narrow\nTested: git diff --check .github/AGENTS.md.\nNot-tested: GitHub Actions, documentation-only change.
Summary
Tests